package com.fight.strive.sys.modules.camunda.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fight.strive.sys.enums.OperateStatusEnum;
import com.fight.strive.sys.modules.camunda.dao.WorkflowTaskMapper;
import com.fight.strive.sys.modules.camunda.dto.WorkflowFormData;
import com.fight.strive.sys.modules.camunda.dto.WorkflowNodesDto;
import com.fight.strive.sys.modules.camunda.dto.WorkflowQueryDto;
import com.fight.strive.sys.modules.camunda.dto.WorkflowVariableDto;
import com.fight.strive.sys.modules.camunda.entity.WorkflowTaskEntity;
import com.fight.strive.sys.modules.camunda.service.WorkflowTaskService;
import com.fight.strive.sys.modules.exception.StriveException;
import com.fight.strive.sys.modules.request.dto.PageRequest;
import com.fight.strive.sys.modules.response.dto.PageData;
import com.fight.strive.sys.modules.sysadmin.utils.SysAdminUtils;
import com.fight.strive.sys.utils.CollectionUtils;
import com.fight.strive.sys.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.FormService;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.IdentityService;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.form.StartFormData;
import org.camunda.bpm.engine.form.TaskFormData;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.camunda.bpm.engine.history.HistoricProcessInstanceQuery;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.FlowNode;
import org.camunda.bpm.model.bpmn.instance.SequenceFlow;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author ZHOUXIANG
 */
@Service
@Slf4j
public class WorkflowTaskServiceImpl implements WorkflowTaskService {

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private TaskService taskService;

    @Resource
    private FormService formService;

    @Resource
    private HistoryService historyService;

    @Resource
    private IdentityService identityService;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private WorkflowTaskMapper workflowTaskMapper;

    @Override
    public WorkflowFormData getStartFormData(String pdId) {
        WorkflowFormData form = new WorkflowFormData();
        StartFormData formData = formService.getStartFormData(pdId);
        form.setProcessId(formData.getProcessDefinition().getId())
                .setProcessKey(formData.getProcessDefinition().getKey())
                .setProcessName(formData.getProcessDefinition().getName())
                .setFormKey(formData.getFormKey())
                .setDeploymentId(formData.getDeploymentId())
                .setFormFields(formData.getFormFields());
        return form;
    }

    @Override
    public WorkflowFormData getTaskFormData(String taskId) {
        WorkflowFormData form = new WorkflowFormData();
        // 查询出的表单字段自动关联variables中的变量值
        TaskFormData taskFormData = formService.getTaskFormData(taskId);
        form.setFormKey(taskFormData.getFormKey())
                .setDeploymentId(taskFormData.getDeploymentId())
                .setProcessId(taskFormData.getTask().getProcessDefinitionId())
                .setFormFields(taskFormData.getFormFields())
                .setTaskId(taskFormData.getTask().getId())
                .setTaskName(taskFormData.getTask().getName());
        return form;
    }

    @Override
    public void startProcess(String key, WorkflowVariableDto dto) {
        List<String> tenantIds = new ArrayList<>();
        tenantIds.add(String.valueOf(SysAdminUtils.getCurrentTenantId()));
        // 设置流程发起人，其中租户必须要有。
        if (StringUtils.isNotBlank(dto.getStartUser())) {
            identityService.setAuthentication(
                    dto.getStartUser(), new ArrayList<>(), tenantIds);
        }
        try {
            // 发起流程
            ProcessInstance instance = runtimeService
                    .startProcessInstanceByKey(key, dto.getVariables());
            // 获取当前任务，会签可能会有多个
            List<Task> tasks = taskService.createTaskQuery()
                    .processInstanceId(instance.getProcessInstanceId())
                    .list();
            if (CollectionUtils.isNotEmpty(tasks)) {
                tasks.forEach(t -> {
                    // 为当前任务分派用户
                    if (StringUtils.isNotBlank(dto.getAssignee())) {
                        taskService.setAssignee(t.getId(), dto.getAssignee());
                    }
                    // 为当前任务添加候选用户
                    if (StringUtils.isNotBlank(dto.getCandidateUsers())) {
                        List<String> users = WorkflowVariableDto
                                .getIdListByStr(dto.getCandidateUsers());
                        users.forEach(u -> taskService.addCandidateUser(t.getId(), u));
                    }
                    // 为当前任务添加候选组
                    if (StringUtils.isNotBlank(dto.getCandidateGroups())) {
                        List<String> groups = WorkflowVariableDto
                                .getIdListByStr(dto.getCandidateGroups());
                        groups.forEach(g -> taskService.addCandidateGroup(t.getId(), g));
                    }
                });
            }
        } catch (Exception e) {
            throw new StriveException("发起流程失败：".concat(e.getMessage()));
        }
    }

    @Override
    public PageData<HistoricProcessInstance> getMyProcessInstances(
            PageRequest<WorkflowQueryDto> pageRequest) {
        WorkflowQueryDto condition = pageRequest.getCondition();
        HistoricProcessInstanceQuery query =
                historyService.createHistoricProcessInstanceQuery();
        // 根据流程发起人查询
        if (StringUtils.isNotBlank(condition.getStartUser())) {
            query.startedBy(condition.getStartUser());
        }
        // 根据流程名称查询
        if (StringUtils.isNotBlank(condition.getProcessDefinitionName())) {
            query.processDefinitionNameLike(
                    "%".concat(condition.getProcessDefinitionName()).concat("%"));
        }
        // 判断流程是否结束
        if (StringUtils.equalsIgnoreCase(condition.getIsFinished(),
                OperateStatusEnum.Y.name())) {
            query.finished();
        }
        // 分页查询
        List<HistoricProcessInstance> list = query
                        .listPage((int) pageRequest.getStart(0),
                                (int) pageRequest.getRows());
        long total = query.count();
        PageData<HistoricProcessInstance> pageData = new PageData<>(pageRequest);
        pageData.setTotal(total).calcPages().setContent(list);
        return pageData;
    }

    @Override
    public WorkflowNodesDto getNodeIds(String proInsId) {
        HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(proInsId).singleResult();
        WorkflowNodesDto dto = new WorkflowNodesDto();
        List<HistoricActivityInstance> actList =
                historyService.createNativeHistoricActivityInstanceQuery()
                        .sql("SELECT * FROM `act_hi_actinst` where PROC_INST_ID_ = #{proInsId} order by SEQUENCE_COUNTER_ asc")
                        .parameter("proInsId",proInsId)
                        .list();
        BpmnModelInstance bpmnModel =
                repositoryService.getBpmnModelInstance(hpi.getProcessDefinitionId());
        if (CollectionUtils.isNotEmpty(actList)) {
            for (int i = 0; i < actList.size(); i++) {
                dto.getActIns().add(actList.get(i));
                // 获取该活动的流程图节点
                FlowNode flowNode = bpmnModel.getModelElementById(
                        actList.get(i).getActivityId());
                // 找到该节点的流出箭头元素
                Collection<SequenceFlow> sequenceFlows = flowNode.getOutgoing();
                if (i < actList.size() - 1) {
                    HistoricActivityInstance nextAct = actList.get(i+1);
                    // 判断流出箭头的流入节点是否在这些节点中
                    sequenceFlows.forEach(flow -> {
                        if (flow.getTarget().getId().equals(nextAct.getActivityId())) {
                            dto.getSeqIds().add(flow.getId());
                        }
                    });
                }
            }
        }
        return dto;
    }

    @Override
    public PageData<WorkflowTaskEntity> getTodoTaskList(
            PageRequest<WorkflowQueryDto> pageRequest) {
        WorkflowQueryDto condition = pageRequest.getCondition();
        Long tenantId = SysAdminUtils.getCurrentTenantId();
        QueryWrapper<WorkflowTaskEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("t.TENANT_ID_", String.valueOf(tenantId));
        if (StringUtils.isNotBlank(condition.getTaskName())) {
            // 根据任务名称查询
            queryWrapper.like("t.NAME_", condition.getTaskName());
        }
        if (StringUtils.isNotBlank(condition.getProcessDefinitionName())) {
            // 根据流程名称查询
            queryWrapper.like("t.PROC_DEF_NAME_",
                    condition.getProcessDefinitionName());
        }
        queryWrapper.and(qw -> {
            if (StringUtils.isNotBlank(condition.getAssignee())) {
                qw.or().eq("t.ASSIGNEE_", condition.getAssignee());
            }
            if (StringUtils.isNotBlank(condition.getOwner())) {
                qw.or().eq("t.OWNER_", condition.getOwner());
            }
            if (StringUtils.isNotBlank(condition.getCandidateUsers())) {
                qw.or().exists(String.format("select ID_ from `act_ru_identitylink` " +
                        "where TASK_ID_ = t.ID_ and TYPE_ = 'candidate' " +
                        "and USER_ID_ = '%s'", condition.getCandidateUsers()));
            }
            if (StringUtils.isNotBlank(condition.getCandidateGroups())) {
                String groupIds = StringUtils
                        .getSqlInStringByArray(condition.getCandidateGroups());
                qw.or().exists(String.format("select ID_ from `act_ru_identitylink` " +
                        "where TASK_ID_ = t.ID_ and TYPE_ = 'candidate' " +
                        "and GROUP_ID_ in (%s)", groupIds));
            }
            qw.or().apply("1=0");
        });
        IPage<WorkflowTaskEntity> page = pageRequest.buildMybatisPlusPage();
        IPage<WorkflowTaskEntity> iPage =
                workflowTaskMapper.listTodoTaskByPage(page, queryWrapper);
        return new PageData<>(iPage);
    }

    @Override
    public PageData<WorkflowTaskEntity> getCompleteTaskList(PageRequest<WorkflowQueryDto> pageRequest) {
        WorkflowQueryDto condition = pageRequest.getCondition();
        Long tenantId = SysAdminUtils.getCurrentTenantId();
        QueryWrapper<WorkflowTaskEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("t.TENANT_ID_", String.valueOf(tenantId));
        queryWrapper.isNotNull("t.END_TIME_");
        if (StringUtils.isNotBlank(condition.getTaskName())) {
            // 根据任务名称查询
            queryWrapper.like("t.NAME_", condition.getTaskName());
        }
        if (StringUtils.isNotBlank(condition.getProcessDefinitionName())) {
            // 根据流程名称查询
            queryWrapper.like("t.PROC_DEF_NAME_",
                    condition.getProcessDefinitionName());
        }
        queryWrapper.and(qw -> {
            if (StringUtils.isNotBlank(condition.getAssignee())) {
                qw.or().eq("t.ASSIGNEE_", condition.getAssignee());
            }
            if (StringUtils.isNotBlank(condition.getOwner())) {
                qw.or().eq("t.OWNER_", condition.getOwner());
            }
            if (StringUtils.isNotBlank(condition.getCandidateUsers())) {
                qw.or().exists(String.format("select ID_ from `act_hi_identitylink` " +
                        "where TASK_ID_ = t.ID_ and TYPE_ = 'candidate' " +
                        "and USER_ID_ = '%s'", condition.getCandidateUsers()));
            }
            if (StringUtils.isNotBlank(condition.getCandidateGroups())) {
                String groupIds = StringUtils
                        .getSqlInStringByArray(condition.getCandidateGroups());
                qw.or().exists(String.format("select ID_ from `act_hi_identitylink` " +
                        "where TASK_ID_ = t.ID_ and TYPE_ = 'candidate' " +
                        "and GROUP_ID_ in (%s)", groupIds));
            }
            qw.or().apply("1=0");
        });
        IPage<WorkflowTaskEntity> page = pageRequest.buildMybatisPlusPage();
        IPage<WorkflowTaskEntity> iPage =
                workflowTaskMapper.listCompleteTaskByPage(page, queryWrapper);
        return new PageData<>(iPage);
    }

    @Override
    public void claimTask(String taskId, String claimUser) {
        taskService.claim(taskId, claimUser);
    }

    @Override
    public void delegateTask(String taskId, String delegationUser) {
        // 将 taskId 任务委托给 delegationUser，
        // 引擎会自动将owner设置为当前的assignee，
        // 将assignee设置为delegationUser
        taskService.delegateTask(taskId, delegationUser);
    }

    @Override
    public void completeTask(String taskId, WorkflowVariableDto dto) {
        try {
            // 获取完成前任务
            Task task = taskService.createTaskQuery()
                    .taskId(taskId).singleResult();
            // 完成当前任务
            taskService.complete(taskId, dto.getVariables());
            // 获取完成后的任务，会签可能会有多个
            List<Task> tasks = taskService.createTaskQuery()
                    .processInstanceId(task.getProcessInstanceId())
                    .list();
            if (CollectionUtils.isNotEmpty(tasks)) {
                tasks.forEach(t -> {
                    // 为当前任务分派用户
                    if (StringUtils.isNotBlank(dto.getAssignee())) {
                        taskService.setAssignee(t.getId(), dto.getAssignee());
                    }
                    // 为当前任务添加候选用户
                    if (StringUtils.isNotBlank(dto.getCandidateUsers())) {
                        List<String> users = WorkflowVariableDto
                                .getIdListByStr(dto.getCandidateUsers());
                        users.forEach(u -> taskService.addCandidateUser(t.getId(), u));
                    }
                    // 为当前任务添加候选组
                    if (StringUtils.isNotBlank(dto.getCandidateGroups())) {
                        List<String> groups = WorkflowVariableDto
                                .getIdListByStr(dto.getCandidateGroups());
                        groups.forEach(g -> taskService.addCandidateGroup(t.getId(), g));
                    }
                });
            }
        } catch (Exception e) {
            throw new StriveException("完成任务失败：".concat(e.getMessage()));
        }
    }
}
